﻿#include "modbus_manager.h"
#include <QModbusDataUnit>
#include <QModbusReply>
#include <QSerialPort>
#include <QVariant>
#include <algorithm>
#include <QDebug>

ModbusManager::ModbusManager(QObject* parent)
    : QObject(parent)
{
    // 初始化寄存器监控集合和引用计数
    monitoredRegisters_.clear();
    monitorRegisterRefCounts_.clear();
}

ModbusManager::~ModbusManager()
{
    stopSimulation();
}

bool ModbusManager::startSimulation(const QString& serialPortName,
    int baudRate,
    int parity,
    int dataBits,
    int stopBits)
{
    if (isRunning_)
    {
        Q_EMIT logMessage("仿真已经在运行");
        return true;
    }

    // 创建Modbus RTU串口
    modbus_ = new QModbusRtuSerialMaster(this);
    if (!modbus_)
    {
        qDebug() << "创建Modbus客户端失败";
        return false;
    }
    // 配置串口参数
    modbus_->setConnectionParameter(QModbusDevice::SerialPortNameParameter, serialPortName);
    modbus_->setConnectionParameter(QModbusDevice::SerialParityParameter, parity);
    modbus_->setConnectionParameter(QModbusDevice::SerialBaudRateParameter, baudRate);
    modbus_->setConnectionParameter(QModbusDevice::SerialDataBitsParameter, dataBits);
    modbus_->setConnectionParameter(QModbusDevice::SerialStopBitsParameter, stopBits);
    modbus_->setTimeout(1000);
    modbus_->setNumberOfRetries(3);

         qDebug() << serialPortName << baudRate << parity  << dataBits << stopBits;
    // 尝试连接Modbus从站
    if (!modbus_->connectDevice())
    {
        qDebug() << modbus_->error();
        Q_EMIT logMessage("无法连接到 Modbus RTU 从站");
        delete modbus_;
        modbus_ = nullptr;
        return false;
    }
    // 初始化线圈轮询定时器：每300ms触发一次pollModbusCoils
    coilTimer_ = new QTimer(this);
    connect(coilTimer_, &QTimer::timeout, this, &ModbusManager::pollModbusCoils);
    coilTimer_->start(300);

    // 初始化寄存器轮询定时器：每300ms触发一次pollModbusRegisters
    registerTimer_ = new QTimer(this);
    connect(registerTimer_, &QTimer::timeout, this, &ModbusManager::pollModbusRegisters);
    registerTimer_->start(300);
    // 更新运行状态并输出日志
    isRunning_ = true;
    Q_EMIT logMessage(QString("开始仿真：串口 ") + serialPortName);
    return true;
}

void ModbusManager::stopSimulation()
{
    if (!isRunning_) return;
    // 停止并释放线圈轮询定时器
    if (coilTimer_)
    {
        coilTimer_->stop();
        coilTimer_->deleteLater();
        coilTimer_ = nullptr;
    }
    // 停止并释放寄存器轮询定时器
    if (registerTimer_)
    {
        registerTimer_->stop();
        registerTimer_->deleteLater();
        registerTimer_ = nullptr;
    }
    // 断开Modbus连接并释放设备
    if (modbus_)
    {
        modbus_->disconnectDevice();
        modbus_->deleteLater();
        modbus_ = nullptr;
    }
    // 清空缓存
    coilValues_.clear();
    monitoredCoils_.clear();
    monitorCoilCounts_.clear();
    registerValues_.clear();
    monitoredRegisters_.clear();
    monitorRegisterRefCounts_.clear();
    // 更新运行状态并输出日志
    isRunning_ = false;
    Q_EMIT logMessage("停止仿真");
}

// 添加监控地址
void ModbusManager::addMonitoredCoil(int address)
{
    if (address < 0) return;
    int cnt = monitorCoilCounts_.value(address, 0) + 1;
    monitorCoilCounts_[address] = cnt;
    if (cnt == 1)
    {
        monitoredCoils_.insert(address);
    }
}

// 移除监控地址
void ModbusManager::removeMonitoredCoil(int address)
{
    auto it = monitorCoilCounts_.find(address);
    // 地址未在监控集合中，直接返回
    if (it == monitorCoilCounts_.end()) return;
    int cnt = it.value() - 1;
    if (cnt <= 0)
    {
        monitorCoilCounts_.remove(address);
        monitoredCoils_.remove(address);
        coilValues_.remove(address);  // 清理缓存
    }
    else
    {
        it.value() = cnt;
    }
}

// 批量设置监控地址
void ModbusManager::setMonitoredCoils(const QSet<int>& addresses)
{
    monitoredCoils_ = addresses;
    monitorCoilCounts_.clear();
    for (int addr : addresses)
    {
        monitorCoilCounts_[addr] = 1; // 默认每个地址至少被引用一次
    }
    // 清理不再监控的地址的缓存
    QList<int> keys = coilValues_.keys();
    for (int key : keys)
    {
        if (!monitoredCoils_.contains(key))
        {
            coilValues_.remove(key);
        }
    }
}

// 轮询线圈状态，根据监控地址分块读取
void ModbusManager::pollModbusCoils()
{
    if (!modbus_ || monitoredCoils_.isEmpty())
    {
        return;
    }

    // 将集合转为排序向量
    QVector<int> addrs(monitoredCoils_.begin(), monitoredCoils_.end());
    std::sort(addrs.begin(), addrs.end());

    // 分块逻辑
    int start = addrs[0];
    int prev = start;
    for (int i = 1; i < addrs.size(); ++i)
    {
        if (addrs[i] != prev + 1)
        {
            // 发送当前块的读取请求
            sendReadCoilsRequest(start, prev - start + 1);
            start = addrs[i];
        }
        prev = addrs[i];
    }
    // 发送最后一个块
    sendReadCoilsRequest(start, prev - start + 1);
}

// 发送线圈读取请求
void ModbusManager::sendReadCoilsRequest(int startAddress, int count)
{
    qDebug() << "aaaaa" << startAddress<< count;
    QModbusDataUnit readUnit(QModbusDataUnit::Coils, startAddress, count);
    if (auto* reply = modbus_->sendReadRequest(readUnit, 1))
    {
        if (!reply->isFinished())
        {
            connect(reply, &QModbusReply::finished, this, [this, reply, startAddress]() {
                onCoilReadFinished(reply, startAddress);
                });
        }
        else
        {
            reply->deleteLater();
        }
    }
}

// 发送寄存器读取请求
void ModbusManager::sendReadRegistersRequest(int startAddress, int count)
{
    QModbusDataUnit readUnit(QModbusDataUnit::HoldingRegisters, startAddress, count);
    if (auto* reply = modbus_->sendReadRequest(readUnit, 1))
    {
        if (!reply->isFinished())
        {
            connect(reply, &QModbusReply::finished, this, [this, reply, startAddress]() {
                onRegisterReadFinished(reply, startAddress);
                });
        }
        else
        {
            reply->deleteLater();
        }
    }
}

// 轮询保持寄存器：读取从站寄存器值并更新缓存
void ModbusManager::pollModbusRegisters()
{
    if (!modbus_ || monitoredRegisters_.isEmpty())
    {
        return;
    }

    // 将集合转为排序向量
    QVector<int> addrs(monitoredRegisters_.begin(), monitoredRegisters_.end());
    std::sort(addrs.begin(), addrs.end());

    // 分块逻辑
    int start = addrs[0];
    int prev = start;
    for (int i = 1; i < addrs.size(); ++i)
    {
        if (addrs[i] != prev + 1)
        {
            // 发送当前块的读取请求
            sendReadRegistersRequest(start, prev - start + 1);
            start = addrs[i];
        }
        prev = addrs[i];
    }
    // 发送最后一个块
    sendReadRegistersRequest(start, prev - start + 1);
}

void ModbusManager::onCoilReadFinished(QModbusReply* reply, int startAddress)
{
    if (reply->error() == QModbusDevice::NoError)
    {
        auto result = reply->result();
        for (int i = 0; i < static_cast<int>(result.valueCount()); ++i)
        {
            int addr = startAddress + i;
            bool val = result.value(i);
            if (coilValues_.value(addr) != val)
            {
                coilValues_[addr] = val;
                Q_EMIT coilUpdated(addr, val);
            }
        }
    }
    else
    {
        //Q_EMIT logMessage("读取线圈失败: " + reply->errorString());
    }
    reply->deleteLater();
}

void ModbusManager::onRegisterReadFinished(QModbusReply* reply, int startAddress)
{
    if (reply->error() == QModbusDevice::NoError)
    {
        auto result = reply->result();
        for (int i = 0; i < static_cast<int>(result.valueCount()); ++i)
        {
            int addr = startAddress + i;
            quint16 val = result.value(i);
            if (registerValues_.value(addr) != val)
            {
                registerValues_[addr] = val;
                Q_EMIT registerUpdated(addr, val);
            }
        }
    }
    else
    {
        //Q_EMIT logMessage("读取寄存器失败: " + reply->errorString());
    }
    reply->deleteLater();
}

// 向指定地址写入线圈值
void ModbusManager::writeCoil(int address, bool value)
{
    if (!isRunning_ || !modbus_)
    {
        return;
    }
    // 配置写入参数：单个线圈
    QModbusDataUnit writeUnit(QModbusDataUnit::Coils, address, 1);
    writeUnit.setValue(0, value);
    // 发送写入请求，完成后自动释放回复对象
    if (auto* reply = modbus_->sendWriteRequest(writeUnit, 1))
    {
        connect(reply, &QModbusReply::finished, reply, &QObject::deleteLater);
    }
}

// 向指定地址写入寄存器值
void ModbusManager::writeRegister(int address, quint16 value)
{
    if (!isRunning_ || !modbus_)
    {
        return;
    }
    // 配置写入参数：单个寄存器
    QModbusDataUnit writeUnit(QModbusDataUnit::HoldingRegisters, address, 1);
    writeUnit.setValue(0, value);
    qDebug() << u8"writeUnit.setValue:"<< address << value;
    // 发送写入请求，完成后自动释放回复对象
    if (auto* reply = modbus_->sendWriteRequest(writeUnit, 1))
    {
        connect(reply, &QModbusReply::finished, reply, &QObject::deleteLater);
    }
}

void ModbusManager::reinitializeMonitoring()
{
    // 清空当前的监控列表和引用计数
    monitoredCoils_.clear();
    monitorCoilCounts_.clear();
    coilValues_.clear();

    // 重新初始化寄存器值
    registerValues_.clear();
    monitorRegisterRefCounts_.clear();
    registerValues_.clear();
}

// 监控寄存器
void ModbusManager::addMonitoredRegister(int address)
{
    if (address < 0) return;
    int cnt = monitorRegisterRefCounts_.value(address, 0) + 1;
    monitorRegisterRefCounts_[address] = cnt;
    if (cnt == 1)
    {
        monitoredRegisters_.insert(address);
    }
}

// 移除监控寄存器
void ModbusManager::removeMonitoredRegister(int address)
{
    auto it = monitorRegisterRefCounts_.find(address);
    if (it == monitorRegisterRefCounts_.end()) return;
    int cnt = it.value() - 1;
    if (cnt <= 0)
    {
        monitorRegisterRefCounts_.remove(address);
        monitoredRegisters_.remove(address);
        registerValues_.remove(address);  // 清理缓存
    }
    else
    {
        it.value() = cnt;
    }
}

// 批量设置监控寄存器
void ModbusManager::setMonitoredRegisters(const QSet<int>& addresses)
{
    monitoredRegisters_ = addresses;
    monitorRegisterRefCounts_.clear();
    for (int addr : addresses)
    {
        monitorRegisterRefCounts_[addr] = 1; // 默认每个地址至少被引用一次
    }
    // 清理不再监控的地址的缓存
    QList<int> keys = registerValues_.keys();
    for (int key : keys) {
        if (!monitoredRegisters_.contains(key))
        {
            registerValues_.remove(key);
        }
    }
}


// 返回当前运行状态
bool ModbusManager::isRunning() const
{
    return isRunning_;
}
